//
// IPCCenter.cpp
// Usage: Process IPC center, process all ipc request
// CreateDate: 2009-05-16
// Author: MaJiuyue
//
// Send need fix 
//
#include <errno.h>
#include "IPCCenter.h"
#include "../schedule/Process.h"
#include <RequestPackage.h>

CIPCCenter::CIPCCenter()
{
	// Create resource
	Resource * res = new Resource(this);
	ResourceManager.CreateResource(
		res,
		ServiceCallInterface::IPCCenter::ResourceName
	);

	// Register interface
	res->RegisterInterface(
		(Resource::CallInterface2)&CIPCCenter::Send,
		ServiceCallInterface::IPCCenter::SCIID_Send
	);
	res->RegisterInterface(
		(Resource::CallInterface2)&CIPCCenter::Receive,
		ServiceCallInterface::IPCCenter::SCIID_Receive
	);
	res->RegisterInterface(
		(Resource::CallInterface2)&CIPCCenter::SendKNL,
		ServiceCallInterface::IPCCenter::SCIID_SendKNL
	);
}

u32 CIPCCenter::Send(pid_t dst, addr_t msg_vaddr)
{
	Process * cur_proc = GetCurrentProcess();
	Process * dst_proc = NULL;

	// Get dst process
	Resource * res = ResourceManager.RequestResource(
		ServiceCallInterface::ProcessManager::ResourceName
	);
	dst_proc = (Process *)res->CallSCI(
		ServiceCallInterface::ProcessManager::SCIID_GetProcessByPid,
		dst
	);
	
	// if target process not exist, return 'No such process'
	if(dst_proc==NULL)
		return (u32)ESRCH;

	// Get message, set the 'src' field to current process pid
	MESSAGE *msg = (MESSAGE *)cur_proc->GetContext()->GetPhyAddress(msg_vaddr);
	msg->pid = cur_proc->GetPID();
	if(!dst_proc->GetMBox()->PutMessage(USR_MBOX, msg))
		return (u32)EXFULL;

	// Check if dst_proc's mbox is waiting for message form current process
	if(dst_proc->GetMBox()->IsWaitingFor(cur_proc->GetPID()))
	{
		dst_proc->GetMBox()->ResetWait();
		Resource * res = ResourceManager.RequestResource(
			ServiceCallInterface::Scheduler::ResourceName
		);
		res->CallSCI(
			ServiceCallInterface::Scheduler::SCIID_WakeUp,
			dst_proc->GetPID()
		);
	}
	
	return 0;	
}

u32 CIPCCenter::Receive(pid_t src, addr_t msg_vaddr)
{
	Process *cur_proc = GetCurrentProcess();

	// Get message
	MESSAGE *msg = (MESSAGE *)cur_proc->GetContext()->GetPhyAddress(msg_vaddr);
	// Search current process's mbox for a message from src
	bool bFound = cur_proc->GetMBox()->GetMessage(src, msg);

	// If not found, block current process and set wait bit
	if(!bFound)
	{
		Resource * res = ResourceManager.RequestResource(
			ServiceCallInterface::Scheduler::ResourceName
		);
		res->CallSCI(
			ServiceCallInterface::Scheduler::SCIID_Block,
			cur_proc->GetPID()
		);
		return EJUSTRETURN;
	}

	return 0;
}

u32 CIPCCenter::SendKNL(pid_t dst, MESSAGE * msg)
{
	Process * dst_proc = NULL;

	// Get dst process
	Resource * res = ResourceManager.RequestResource(
		ServiceCallInterface::ProcessManager::ResourceName
	);
	dst_proc = (Process *)res->CallSCI(
		ServiceCallInterface::ProcessManager::SCIID_GetProcessByPid,
		dst
	);
	
	// if target process not exist, return 'No such process'
	if(dst_proc==NULL)
		return (u32)ESRCH;

	// Check if dst_proc's mbox is waiting for message form current process
	// if so, copy message immediatilly and wakeup target process
	if(dst_proc->GetMBox()->IsWaitingFor(msg->pid))
	{
		MESSAGE * dst_msg = dst_proc->GetMBox()->GetWaitBuffer();
		memcpy(dst_msg, msg, msg->size);
	
		dst_proc->GetMBox()->ResetWait();
		Resource * res = ResourceManager.RequestResource(
			ServiceCallInterface::Scheduler::ResourceName
		);
		res->CallSCI(
			ServiceCallInterface::Scheduler::SCIID_WakeUp,
			dst_proc->GetPID()
		);
	}
	// else, put message to dst's MBox
	else
	{
		if(!dst_proc->GetMBox()->PutMessage(KNL_MBOX, msg))
			return (u32)EXFULL;
	}

	
	return 0;
}

